#pragma once
#include "public.hpp"

namespace socket_ns
{

    class Socket;
    const int blags = 16;
    using sock_addr = shared_ptr<Socket>;
    
    enum EXITNUM
    {
        SOCK_CREATE_ERROR = 1,
        SOCK_BIND_ERROR,
        SOCK_LISTEN_ERROR
    };

    class Socket
    {
    public:
        virtual void CreateSocket() = 0;
        virtual void BindSocket(Analyze &addr) = 0;
        virtual void ListenSocket() = 0;

        virtual int Accept(Analyze &addr, int* errnocode) = 0;
        virtual bool Connect(Analyze &addr) = 0;
        virtual int Recv(string &buffer) = 0;
        virtual int Send(string &buffer) = 0;

        virtual int getfd() = 0;
        virtual void Setsockopt() = 0;
        
        

        void BuildTcpServerSocket(Analyze &addr)
        {
            CreateSocket();
            Setsockopt();
            BindSocket(addr);
            ListenSocket();
        }

        void BuildTcpClientSocket(Analyze &addr)
        {
            CreateSocket();
            Connect(addr);
        }
    };

   

    class TcpSocket : public Socket
    {
    private:
        int _socketfd;

    public:
        TcpSocket(){}

        TcpSocket(int fd):_socketfd(fd)
        {
        }

        virtual void CreateSocket() override
        {
            _socketfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_socketfd < 0)
            {
                Log(Fatal, "socket create error");
                exit(SOCK_CREATE_ERROR);
            }
            // socket创建成功
            SetNoBlcock(_socketfd);
        }

        virtual void BindSocket(Analyze &addr) override
        {
            // 接下来需要将socket与网络信息进行绑定
            struct sockaddr_in server = addr.getsock();
            // bzero(&server, sizeof(server));

            // server.sin_addr.s_addr = inet_addr(addr.getip().c_str()); // 表示绑定本机任意ip地址
            // server.sin_family = AF_INET;
            // server.sin_port = htons(addr.getport());

            socklen_t len = sizeof(server);
            int bindval = bind(_socketfd, (struct sockaddr *)&server, len);
            if (bindval < 0)
            {
                Log(Fatal, "socket bind error");
                exit(SOCK_BIND_ERROR);
            }
            // 绑定成功
            Log(Info, "socket bind success");
        }

        virtual void ListenSocket() override
        {
            // 监听
            int n = listen(_socketfd, blags);
            if (n < 0)
            {
                Log(Fatal, "listen error");
                exit(SOCK_LISTEN_ERROR);
            }
            Log(Debug, "listen success");
        }

        // 接收连接请求，参数：要求带回客户端信息，返回值：要求返回连接客户端和服务端的fd
        virtual int Accept(Analyze &addr, int* errnocode) override
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);

            // 接收连接请求
            int fd = accept(_socketfd, (struct sockaddr *)&peer, &len);    
            SetNoBlcock(fd);
            *errnocode = errno;

            // cout << "fd: " << fd << endl;
            // cout << "listenfd: " << _socketfd << endl;
            // cout << "reason: " << strerror(errno) << endl; 

            if (fd < 0)
                Log(Warning, "listener accept fail");
            // Log(Info, "listener accept success");
            printf("[%s:%d] socket fd: %d\n", __FILE__, __LINE__, fd);


            return fd;
            // addr = peer;
            // sock_addr sock = make_shared<TcpSocket>(fd);
            // return sock;
        }

        //参数：目标服务器ip，port信息
        virtual bool Connect(Analyze &addr)
        {
            struct sockaddr_in server;
            // 构建目标主机的socket信息
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(addr.getport());
            server.sin_addr.s_addr = inet_addr(addr.getip().c_str());

            int n = connect(_socketfd, (struct sockaddr *)&server, sizeof(server));
            if (n < 0)
            {
                std::cerr << "connect error" << std::endl;
                return false;
            }

            return true;
        }

        virtual int Recv(string &buffer)
        {
            char temp[1024];
            bzero(temp, sizeof(temp));

            int n = recv(_socketfd, temp, sizeof(temp)-1, 0);

            if(n > 0) buffer += temp;
            return n;
        }

        virtual int Send(string &buffer)
        {
            return send(_socketfd, buffer.c_str(), buffer.size(), 0);
        }

        virtual int getfd()
        {
            return _socketfd;
        }

        virtual void Setsockopt()
        {
            int opt = 1;
            setsockopt(_socketfd, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt));
        }

        ~TcpSocket()
        {
            close(_socketfd);
        }
    };

};
